package cn.pzaily.fealetter.service.impl;

import cn.pzaily.crab.tools.core.exception.ExceptionUtils;
import cn.pzaily.crab.tools.core.utils.ObjectUtils;
import cn.pzaily.crab.tools.core.utils.StringUtils;
import cn.pzaily.fealetter.dto.BatchParamsDTO;
import cn.pzaily.fealetter.dto.ParamsDTO;
import cn.pzaily.fealetter.dto.SendDTO;
import cn.pzaily.fealetter.dto.base.BaseParamsDTO;
import cn.pzaily.fealetter.entity.*;
import cn.pzaily.fealetter.enumeration.BlackListType;
import cn.pzaily.fealetter.enumeration.RedisPropertiesKey;
import cn.pzaily.fealetter.enumeration.TemplateType;
import cn.pzaily.fealetter.exception.FealetterException;
import cn.pzaily.fealetter.mapper.ReceiveLogMapper;
import cn.pzaily.fealetter.service.*;
import cn.pzaily.fealetter.utils.EncryptionUtils;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @PackageName: cn.pzaily.fealetter.service.impl
 * @Description:
 * @Author: Pang
 * @Date: 2021-11-21 17:38
 */
@Service
public class SendServiceImpl implements SendService {

    @Autowired
    private PlatformService platformService;

    @Autowired
    private BlackListService blackListService;

    @Autowired
    private SignatureService signatureService;

    @Autowired
    private TemplateService templateService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TimingPushService timingPushService;

    @Autowired
    private ReceiveLogMapper receiveLogMapper;


    @Override
    public PlatformEntity checkAccessKeyId(String accessKeyId) {
        PlatformEntity platformEntity = platformService.findPlatformEntityByAccessKeyId(accessKeyId);
        if (ObjectUtils.isNull(platformEntity)) {
            throw new FealetterException("系统未注册");
        }
        if (platformEntity.getIsActive() == 0) {
            throw new FealetterException("系统不可用");
        }
        return platformEntity;
    }

    @Override
    public void checkAuth(String timestamp, String accessKeyId, String accessKeySecret, String accessEncryption) {
        String encode = EncryptionUtils.encode(timestamp, accessKeyId, accessKeySecret);
        if (accessEncryption.equals(encode)) {
            return;
        }
        throw new FealetterException("鉴权失败！");
    }

    @Override
    public void checkBlack(String mobile) {
        if (StringUtils.isEmpty(mobile)) {
            throw new FealetterException("手机号为空");
        }

        /*if (!PatternUtils.isMobileNumber(mobile)) {
            throw new FealetterException("手机号格式不正确");
        }*/

        List<String> phones = blackListService.findBlackListByType(BlackListType.MESSAGE.getCode());
        if (phones.contains(mobile)) {
            throw new FealetterException(mobile + "为黑名单手机号");
        }
    }

    @Override
    public List<String> checkTemplateAndSignature(String template, String signature) {
        TemplateEntity templateByCode = templateService.findTemplateByCode(template);

        if (ObjectUtils.isNull(template)) {throw new FealetterException("模板不存在");}

        SignatureEntity signatureByCode = signatureService.findSignatureByCode(signature);

        if (ObjectUtils.isNull(signatureByCode)) {throw new FealetterException("签名不存在");}

        List<ConfigEntity> configByTemplateAndSignature = configService.findConfigByTemplateAndSignature(templateByCode.getId(), signatureByCode.getId());

        if (CollectionUtils.isEmpty(configByTemplateAndSignature)) {
            throw new FealetterException("没有找到支持该模板和签名的通道");
        }

        return configByTemplateAndSignature.stream().map(item -> item.getId()).collect(Collectors.toList());


    }

    @Override
    public void checkoutSendTime(String sendTime) {
        if (StringUtils.isNotEmpty(sendTime)) {
            LocalDateTime localDateTime =
                    LocalDateTime.parse(sendTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));

            LocalDateTime nowDateTime =
                    LocalDateTime.now().plusMinutes(1L).withSecond(0).minusMinutes(0).withNano(0);

            if (localDateTime.compareTo(nowDateTime) <= 0) {
                throw new FealetterException("发送时间过于接近当前时间，无法发送");
            }
        }
    }

    @Override
    public TemplateEntity checkParams(String templateCode, Map<String, String> params) {
        TemplateEntity templateByCode = templateService.findTemplateByCode(templateCode);
        String content = StringUtils.renderString(templateByCode.getContent(), params);
        if (content.indexOf("${") > 0) {
            throw new FealetterException("参数不匹配" + content);
        }
        return templateByCode;
    }

    @Override
    public void send(ParamsDTO paramsDTO) {
        PlatformEntity platformEntity = check(paramsDTO);
        SendDTO sendDTO = copyPropertiesToSendDTO(paramsDTO.getMobile(),
                paramsDTO.getSignature(), paramsDTO.getTemplate(),
                paramsDTO.getSendTime(), paramsDTO.getParams());
        sendMessage(sendDTO, platformEntity);
    }

    @Override
    public void batchSend(BatchParamsDTO batchParamsDTO) {

        PlatformEntity platformEntity = check(batchParamsDTO);

        Iterator<String> mobiles = batchParamsDTO.getMobile().iterator();
        Iterator<String> signatures = batchParamsDTO.getSignature().iterator();
        Iterator<String> templates = batchParamsDTO.getTemplate().iterator();
        Iterator<LinkedHashMap<String, String>> params = batchParamsDTO.getParams().iterator();

        if (StringUtils.isEmpty(batchParamsDTO.getBatchCode())) {
            batchParamsDTO.setBatchCode(UUID.randomUUID().toString().replace("-", ""));
        }

        String mobile = null;
        String signature = null;
        String template = null;
        LinkedHashMap<String, String> param = null;
        String sendTime = null;

        while (mobiles.hasNext() || signatures.hasNext() || templates.hasNext() || params.hasNext()) {
            if (mobiles.hasNext()) {mobile = mobiles.next();}
            if (signatures.hasNext()) {signature = signatures.next();}
            if (templates.hasNext()) {template = templates.next();}
            if (params.hasNext()) {param = params.next();}

            SendDTO sendDTO = copyPropertiesToSendDTO(mobile, signature, template, sendTime, param);
            sendMessage(sendDTO, platformEntity);
        }

    }


    private PlatformEntity check(BaseParamsDTO paramsDTO) {
        // 1. 检验定时发送时间
        checkoutSendTime(paramsDTO.getSendTime());

        // 2. 检验系统是否注册
        PlatformEntity platformEntity = checkAccessKeyId(paramsDTO.getAccessKeyId());

        if (platformEntity.getNeedAuth() == 1) {
            // 3. 检查认证值
            checkAuth(
                    paramsDTO.getTimestamp(),
                    platformEntity.getAccessKeyId(),
                    platformEntity.getAccessKeySecret(),
                    paramsDTO.getEncryption()
            );
        }
        return platformEntity;
    }


    private void sendMessage(SendDTO sendDTO, PlatformEntity platformEntity) {
        // 1. 校验手机号是否在黑名单中
        checkBlack(sendDTO.getMobile());

        // 2. 校验模板和签名
        List<String> configIds = checkTemplateAndSignature(sendDTO.getTemplate(), sendDTO.getSignature());
        sendDTO.setConfigIds(configIds);

        // 3. 校验参数
        TemplateEntity templateEntity = checkParams(sendDTO.getTemplate(), sendDTO.getParams());

        // 4. 调用发送接口
        pushMessage(templateEntity, sendDTO, platformEntity);

    }



    private void pushMessage(TemplateEntity templateEntity, SendDTO sendDTO, PlatformEntity platformEntity) {
        /**
         * TODO HTTP短信接收服务，将短信信息保存到数据库或Redis
         * 短信分为实时发送和定时发送,实时发送根据消息类型进行优先级排列
          */

        // 开始时间
        long start = System.currentTimeMillis();
        // 短信接收日志
        ReceiveLogEntity receiveLogEntity = new ReceiveLogEntity();
        receiveLogEntity.setApiLogId(UUID.randomUUID().toString().replaceAll("-", "").toLowerCase());

        try {
            sendDTO.setLogId(receiveLogEntity.getApiLogId());
            String sendDTOJSON = JSON.toJSONString(sendDTO);

            String sendTime = sendDTO.getSendTime();
            if (StringUtils.isEmpty(sendTime)) {
                // 实时发送
                if (templateEntity.getType() == TemplateType.VERIFICATION.getCode()) {
                    // 将信息插入到优先级中
                    redisTemplate.opsForList().leftPush(RedisPropertiesKey.TOPIC_HIGH_MESSAGE, sendDTOJSON);
                } else {
                    // 将信息插入到低先级中
                    redisTemplate.opsForList().leftPush(RedisPropertiesKey.TOPIC_LOW_MESSAGE, sendDTOJSON);
                }
            } else {
                // 定时发送
                TimingPushEntity timingPushEntity = new TimingPushEntity();
                copyPropertiesToTimingPush(sendDTO, timingPushEntity);
                timingPushService.save(timingPushEntity);
            }
            receiveLogEntity.setStatus(1);

        } catch (Exception e) {
            receiveLogEntity.setStatus(0);
            receiveLogEntity.setError(ExceptionUtils.getErrorStackTrace(e));
        } finally {
            // 将短信服务接收的日志插入到数据库中
            copyPropertiesToReceiveLog(sendDTO, platformEntity, start, receiveLogEntity);
            receiveLogMapper.insert(receiveLogEntity);
        }
    }

    /**
     * 为接收日志设置属性
     * @param sendDTO
     * @param platformEntity
     * @param start
     * @param receiveLogEntity
     */
    private void copyPropertiesToReceiveLog(SendDTO sendDTO, PlatformEntity platformEntity, long start, ReceiveLogEntity receiveLogEntity) {
        receiveLogEntity.setMobile(sendDTO.getMobile());
        receiveLogEntity.setRequest(JSON.toJSONString(sendDTO));
        receiveLogEntity.setConfigIds(StringUtils.join(sendDTO.getConfigIds(), ","));
        receiveLogEntity.setPlatformId(platformEntity.getId());
        receiveLogEntity.setPlatformName(platformEntity.getName());
        receiveLogEntity.setUseTime(System.currentTimeMillis() - start);
        receiveLogEntity.setBusiness(sendDTO.getBatchCode());
        receiveLogEntity.setTemplate(sendDTO.getTemplate());
        receiveLogEntity.setSignature(sendDTO.getSignature());
    }

    /**
     *
     * @param sendDTO
     * @param timingPushEntity
     */
    private void copyPropertiesToTimingPush(SendDTO sendDTO, TimingPushEntity timingPushEntity) {
        timingPushEntity.setTiming(sendDTO.getSendTime());
        timingPushEntity.setMobile(sendDTO.getMobile());
        timingPushEntity.setSignature(sendDTO.getSignature());
        timingPushEntity.setTemplate(sendDTO.getTemplate());
        timingPushEntity.setRequest(JSON.toJSONString(sendDTO));
    }

    /**
     *
     * @param mobile
     * @param signature
     * @param template
     * @param sendTime
     * @param param
     * @return
     */
    private SendDTO copyPropertiesToSendDTO(String mobile,
                                         String signature,
                                         String template,
                                         String sendTime,
                                         Map<String, String> param) {
        SendDTO sendDTO = new SendDTO();
        sendDTO.setSendTime(sendTime);
        sendDTO.setMobile(mobile);
        sendDTO.setSignature(signature);
        sendDTO.setTemplate(template);
        sendDTO.setParams(param);
        return sendDTO;
    }

}
